﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Drawing;

namespace LowLevelGraphics.Filter
{
    /// <summary>
    /// Find Contours
    /// This will help to find contours, the algorithm is though different from an edge filter
    /// It uses a color tolerance of RGB which is used to estimate if a line belongs to a contour
    /// </summary>
    public class FindContours : BaseForeBackgroundColorFilter
    {
        protected int m_nToleranceR = 15;
        protected int m_nToleranceG = 15;
        protected int m_nToleranceB = 15;

        /// <summary>
        /// Initializes a new instance of the <see cref="FindContours"/> class.
        /// </summary>
        public FindContours()
            : this(Color.Red, Color.Black, 15, 15, 15)
        {
        }

        /// <summary>
        /// Initializes a new instance of the <see cref="FindContours"/> class.
        /// </summary>
        /// <param name="_colorFront">The _color front.</param>
        /// <param name="_colorBack">The _color back.</param>
        /// <param name="nTolerance">The n tolerance.</param>
        public FindContours(Color _colorFront, Color _colorBack, int nTolerance)
            : this(Color.Red, Color.Black, nTolerance, nTolerance, nTolerance)
        {
        }

        /// <summary>
        /// Initializes a new instance of the <see cref="FindContours"/> class.
        /// </summary>
        /// <param name="_colorFront">The _color front.</param>
        /// <param name="_colorBack">The _color back.</param>
        /// <param name="nToleranceR">The n tolerance R.</param>
        /// <param name="nToleranceG">The n tolerance G.</param>
        /// <param name="nToleranceB">The n tolerance B.</param>
        public FindContours(Color _colorFront, Color _colorBack, int nToleranceR, int nToleranceG, int nToleranceB)
        {
            m_ColorForeground = _colorFront;
            m_ColorBackground = _colorBack;

            m_nToleranceR = nToleranceR;
            m_nToleranceG = nToleranceG;
            m_nToleranceB = nToleranceB;
        }

        /// <summary>
        /// Initializes a new instance of the <see cref="FindContours"/> class.
        /// </summary>
        /// <param name="_findContours">The _find contours.</param>
        internal FindContours(FindContours _findContours)
        {
            this.m_ColorForeground = _findContours.m_ColorForeground;
            this.m_ColorBackground = _findContours.m_ColorBackground;
            this.m_nToleranceR = _findContours.m_nToleranceR;
            this.m_nToleranceG = _findContours.m_nToleranceG;
            this.m_nToleranceB = _findContours.m_nToleranceB;
        }


        /// <summary>
        /// Gets or sets the R.
        /// </summary>
        /// <value>The R.</value>
        public int R
        {
            get { return m_nToleranceR; }
            set { m_nToleranceR = value; }
        }

        /// <summary>
        /// Gets or sets the G.
        /// </summary>
        /// <value>The G.</value>
        public int G
        {
            get { return m_nToleranceG; }
            set { m_nToleranceG = value; }
        }

        /// <summary>
        /// Gets or sets the B.
        /// </summary>
        /// <value>The B.</value>
        public int B
        {
            get { return m_nToleranceB; }
            set { m_nToleranceB = value; }
        }

        /// <summary>
        /// Gets or sets the front color.
        /// </summary>
        /// <value>The color front.</value>
        /// <summary>
        /// Todo: better detection of contours.
        /// </summary>
        /// <param name="_bitmap"></param>
        public override UnsafeBitmap Execute(UnsafeBitmap _bitmap)
        {
            UnsafeBitmap bitmap = _bitmap;
            List<Color> aColor = new List<Color>();

            Dictionary<Point, Color> aPoint = new Dictionary<Point, Color>();
            aColor.Add(bitmap.GetPixel(0, 0));
            int nCount = 1;

            Color colorLeft = Color.Transparent;
            Color colorPrev = Color.Transparent;
            Color colorToSet = Color.Transparent;

            int nHeight = bitmap.Height;
            int nWidth = bitmap.Width;

            int x = 0;
            int y = 0;

            for (y = 1; y < nHeight; y++)
            {
                aPoint.Clear();
                for (x = 0; x < nWidth; x++)
                {
                    colorLeft = bitmap.GetPixel(x, y);
                    aPoint.Add(new Point(x, y), colorLeft);
                    aColor.Add(colorLeft);
                    colorPrev = aColor[nCount - 1];
                    if (!ColorFunctions.IsInColorTolerance(colorLeft, colorPrev, m_nToleranceR, m_nToleranceG, m_nToleranceB))
                    {
                        aPoint[new Point(x, y)] = Color.Red;
                    }
                    if (!ColorFunctions.IsInColorTolerance(colorLeft, bitmap.GetPixel(x, y - 1), 15, 15, 15))
                    {
                        aPoint[new Point(x, y - 1)] = Color.Red;
                    }
                    nCount++;
                }

                for (x = 0; x < bitmap.Width; x++)
                {
                    if (aPoint[new Point(x, y)] != Color.Red)
                    {
                        colorToSet = m_ColorBackground;
                    }
                    else
                    {
                        colorToSet = m_ColorForeground;
                    }
                    bitmap.SetPixel(x, y, colorToSet);

                    int nX2 = bitmap.Width - 1 - x;
                    if (aPoint[new Point(nX2, y)] != Color.Red)
                    {
                        colorToSet = m_ColorBackground;
                    }
                    else
                    {
                        colorToSet = m_ColorForeground;
                    }
                    bitmap.SetPixel(nX2, y, colorToSet);
                }
            }

            return _bitmap;
        }

        /// <summary>
        /// This is used to clone a filter, each filter must implement it's own clone functionality
        /// </summary>
        /// <returns></returns>
        public override object Clone()
        {
            return new FindContours(this);
        }
    }
}
